/*
    DUMP.C

Written by DavidW
Started on 27/11/91

*/

#include <p_std.h>
#include <p_file.h>
#include <wlib.h>
#include <hwif.h>

GLREF_D UWORD DatLocked;
GLREF_D TEXT *DatUsedPathNamePtr;


LOCAL_D TEXT *cmds[]=
    {
    "oOpen",
    "wWrite",
    "fFind text",
    "bFind bytes",
    "aFind again",
    "gGoto",
    "xExit",
    NULL
    };

#define FILE_ITEMS          2
#define FIND_ITEMS          3
#define SPECIAL_ITEMS       2

#define ITEMS_BEFORE_FILE       0
#define ITEMS_BEFORE_FIND       FILE_ITEMS
#define ITEMS_BEFORE_SPECIAL    (ITEMS_BEFORE_FIND+FIND_ITEMS)

LOCAL_D H_MENU_DATA mdata[]=
    {
    "File",FILE_ITEMS,
    "Find",FIND_ITEMS,
    "Special",SPECIAL_ITEMS,
    NULL
    };

GLDEF_D TEXT ** _cmds=(&cmds[0]);
GLDEF_D H_MENU_DATA * _mdata=(&mdata[0]);


LOCAL_D TEXT filename[P_FNAMESIZE+2];
LOCAL_D VOID *fcb;

LOCAL_D LONG lpos;
LOCAL_D LONG last;

LOCAL_D UWORD DisplayPending;

LOCAL_D VOID *wfcb;

LOCAL_D TEXT findt[14]={0};
LOCAL_D UWORD caset=1;
LOCAL_D TEXT findb[14]={0};
LOCAL_D TEXT *pfind;
LOCAL_D UWORD casef;
LOCAL_D LONG fpos=(-1);


LOCAL_C VOID Check(INT val)
    {
    TEXT buf[64];

    if (val<0)
        {
        wCancelBusyMsg();
        uForceToFront();
        DatLocked=TRUE;
        p_errs(&buf[0],val);
        wsAlertW(WS_ALERT_CLIENT,&buf[0],"Terminating application",0);
        p_exit(0);
        }
    }

LOCAL_C VOID DoSeek(INT sense,LONG *ppos)
    {
    Check(p_seek(fcb,sense,ppos));
    }

LOCAL_C INT WriteLine(INT ignore,TEXT *pb)
    {
    return(p_write(wfcb,pb,38));
    }

LOCAL_C INT PrintLine(INT lineno,TEXT *pb)
    {
    P_RECT box;

    box.tl.x=6;
    box.tl.y=4+9*lineno;
    box.br.x=234;
    box.br.y=box.tl.y+9;
    gPrintBoxText(&box,8,G_TEXT_ALIGN_LEFT,0,pb,38);
    return(0);
    }

LOCAL_C VOID Output(INT len,INT nlines,INT DoLine(INT,TEXT *))
    {
    TEXT inbuf[64];
    TEXT outbuf[38];
    INT i;
    LONG loclpos;
    INT linelen;
    TEXT *pin;
    TEXT *pout1;
    TEXT *pout2;
    INT val;

    Check(p_read(fcb,&inbuf[0],len));
    pin=(&inbuf[0]);
    loclpos=lpos;
    for (i=0; i<nlines; i++)
        {
        p_atos(&outbuf[0],"%05lx",loclpos);
        loclpos+=8;
        linelen=(len<8? len: 8);
        len-=linelen;
        pout1=(&outbuf[5]);
        pout2=(&outbuf[30]);
        p_bfil(pout1,33,32);
        while (linelen--)
            {
            val=(*pin++);
            p_atos(pout1," %02x",val);
            pout1+=3;
            *pout2++=(val<32? 8: val);
            }
        *pout1=32;
        if (DoLine(i,&outbuf[0]))
            break;
        }
    }

LOCAL_C VOID Display(VOID)
    {
    LONG ldiff;

    DoSeek(P_FABS,&lpos);
    ldiff=last-lpos;
    Output((ldiff<64? (INT)ldiff: 64),8,PrintLine);
    DisplayPending=FALSE;
    }

LOCAL_C VOID ResetDisplay(VOID)
    {
    last=0;
    DoSeek(P_FEND,&last);
    lpos=0;
    Display();
    }

LOCAL_C INT TryOpenFile(TEXT *pb,VOID **newfcb)
    {
    return(uErrorValue(p_open(newfcb,pb,P_FOPEN|P_FRANDOM)));
    }

LOCAL_C VOID ChangeOverTo(TEXT *newname,VOID *newfcb)
    {
    p_close(fcb);
    fcb=newfcb;
    p_scpy(&filename[0],newname);
    hSetUpStatusNames(&filename[0]);
    wsUpdate(WS_UPDATE_NAME);
    ResetDisplay();
    }

LOCAL_C VOID SetupFilename(TEXT *pb)
    {
    p_bcpy(pb+1,DatUsedPathNamePtr,*pb=p_slen(DatUsedPathNamePtr));
    }

LOCAL_C VOID OpenFile(VOID)
    {
    H_DI_FSEL open;
    TEXT openbuf[P_FNAMESIZE+2];
    VOID *newfcb;

    SetupFilename(&openbuf[0]);
    do
        {
        if (uOpenDialog("Dump"))
            return;
        open.flags=H_FILE_PICK_SELECTOR;
        open.fname=(&openbuf[0]);
        if (uAddDialogItem(H_DIALOG_FSEL,"File:",&open))
            return;
        if (uRunDialog()<=0)
            return;
        openbuf[1+openbuf[0]]=0;
        } while (TryOpenFile(&openbuf[1],&newfcb));
    ChangeOverTo(&openbuf[1],newfcb);
    }

LOCAL_C VOID WriteFile(VOID)
    {
    H_DI_FSEL write;
    TEXT writebuf[P_FNAMESIZE+2];
    LONG beg;
    LONG end;
    LONG diff;
    H_DI_NUMBER numbeg;
    H_DI_NUMBER numend;
    INT len;
    INT stop;

    if (uErrorValue(p_fparse(".dmp",DatUsedPathNamePtr,&writebuf[1],NULL)))
        return;
    writebuf[0]=p_slen(&writebuf[1]);
    write.fname=(&writebuf[0]);
    write.flags=H_FILE_NEW_EDITOR;
    beg=0;
    end=last;
    numbeg.value=(&beg);
    numbeg.low=0;
    numbeg.high=last;
    numend.value=(&end);
    numend.low=0;
    numend.high=last;
    FOREVER
        {
        if (uOpenDialog("Write dump to file"))
            return;
        if (uAddDialogItem(H_DIALOG_FSEL,"File:",&write))
            return;
        if (uAddDialogItem(H_DIALOG_NUMBER,"From offset",&numbeg))
            return;
        if (uAddDialogItem(H_DIALOG_NUMBER,"To offset",&numend))
            return;
        if (uRunDialog()<=0)
            return;
        diff=end-beg;
        if (diff<=0)
            {
            wInfoMsg("`To' offset must exceed `From' offset");
            continue;
            }
        writebuf[1+writebuf[0]]=0;
        if (!uErrorValue(p_open(&wfcb,
                                &writebuf[1],P_FTEXT|P_FREPLACE|P_FUPDATE)))
            break;
        }
    DoSeek(P_FABS,&beg);
    stop=FALSE;
    FOREVER
        {
        if (diff>64)
            len=64;
        else
            {
            len=(INT)diff;
            stop=TRUE;
            }
        Output(len,len/8,WriteLine);
        if (stop)
            break;
        diff-=64;
        }
    p_close(wfcb);
    wInfoMsg("Dump written to file");
    }

LOCAL_C VOID GotoLpos(VOID)
    {
    H_DI_NUMBER num;

    if (last<=64)
        {
        wInfoMsg("Whole file is already visible");
        return;
        }
    if (uOpenDialog(NULL))
        return;
    num.value=(&lpos);
    num.low=0;
    num.high=last-64;
    if (uAddDialogItem(H_DIALOG_NUMBER,"Goto",&num))
        return;
    if (uRunDialog()<=0)
        return;
    Display();
    }

LOCAL_C VOID HighlightBlock(INT x,INT y)
    {
    P_RECT box;

    box.tl.y=4+9*y;
    box.br.y=box.tl.y+9;
    if (&findb[0]==pfind)
        {
        box.tl.x=41+18*x;
        box.br.x=box.tl.x+13;
        }
    else
        {
        box.tl.x=185+6*x;
        box.br.x=box.tl.x+7;
        }
    gClrRect(&box,G_TRMODE_INV);
    wFlush();
    p_sleep(10);
    gClrRect(&box,G_TRMODE_INV);
    }

LOCAL_C VOID HighlightFpos(VOID)
    {
    INT offset;

    if (fpos<lpos)
        lpos=fpos;
    else if (fpos>lpos+63)
        {
        lpos=fpos;
        if (lpos>last-64)
            lpos=last-64;
        }
    Display();
    offset=(INT)(fpos-lpos);
    HighlightBlock(offset%8,offset/8);
    }

LOCAL_C INT DoSearch(LONG *ptot)
    {
    TEXT buf[256];
    INT toread;
    TEXT *pb;
    INT j;
    INT offset;
    INT len;

    DoSeek(P_FABS,ptot);
    toread=256;
    pb=(&buf[0]);
    offset=0;
    wSetBusyMsg("Searching",W_CORNER_BOTTOM_LEFT|2);
    FOREVER
        {
        len=p_read(fcb,pb,toread);
        if (len==E_FILE_EOF)
            break;
        Check(len);
        if (casef==2)
            j=p_bsub(&buf[0],len+offset,pfind+1,*pfind);
        else
            j=p_bsubi(&buf[0],len+offset,pfind+1,*pfind);
        if (j>=0)
            {
            fpos=(*ptot)+j;
            wCancelBusyMsg();
            HighlightFpos();
            return(0);
            }
        if (len<toread)
            break;
        *ptot+=toread+offset-12;
        pb=(TEXT *)p_bcpy(&buf[0],&buf[256-12],12);
        offset=12;
        toread=256-12;
        }
    wCancelBusyMsg();
    return(-1);
    }

LOCAL_C VOID SearchAll(VOID)
    {
    LONG tot;

    tot=0;
    if (DoSearch(&tot))
        {
        wInfoMsg("Not found");
        fpos=(-1);
        }
    }

LOCAL_C VOID FindText(VOID)
    {
    H_DI_EDIT ed;

    if (uOpenDialog("Find"))
        return;
    ed.str=(&findt[0]);
    ed.len=12;
    if (uAddDialogItem(H_DIALOG_EDIT,"Text",&ed))
        return;
    if (uAddChoiceList("Case sensitive",&caset,"No","Yes",NULL))
        return;
    if (uRunDialog()<=0)
        return;
    pfind=(&findt[0]);
    casef=caset;
    SearchAll();
    }

LOCAL_C VOID LoadBytes(TEXT *pout)
    {
    INT len;
    TEXT *pin;

    len=findb[0];
    *pout++=len*2;
    pin=(&findb[1]);
    while (len--)
        {
        p_atos(pout,"%02x",*pin++);
        pout+=2;
        }
    }

LOCAL_C INT RecoverBytes(TEXT *pin)
    {
    TEXT buf[30];
    INT len;
    TEXT *pout;
    TEXT *newpin;
    UBYTE tmp;

    if (p_bloc(pin+1,*pin,32)>=0)
        {
        wInfoMsg("Don't type any spaces");
        return(-1);
        }
    if (*pin&1)
        {
        wInfoMsg("Type bytes as two characters each");
        return(-1);
        }
    len=buf[0]=(*pin++)/2;
    pout=(&buf[1]);
    while (len--)
        {
        newpin=pin+2;
        tmp=(*newpin);
        *newpin=0;
        if (p_stog(&pin,(UWORD *)pout,16) || pin!=newpin)
            {
            p_atos(&buf[0],"Conversion failure at %s",newpin-2);
            wInfoMsg(&buf[0]);
            *newpin=tmp;
            return(-1);
            }
        *newpin=tmp;
        pout++;
        }
    p_bcpy(&findb[0],&buf[0],buf[0]+1);
    return(0);
    }

LOCAL_C VOID FindBytes(VOID)
    {
    TEXT bytes[26];
    H_DI_EDIT ed;

    LoadBytes(&bytes[0]);
    ed.str=(&bytes[0]);
    ed.len=24;
    do
        {
        if (uOpenDialog("Find"))
            return;
        if (uAddDialogItem(H_DIALOG_EDIT,"Bytes",&ed))
            return;
        if (uRunDialog()<=0)
            return;
        } while (RecoverBytes(&bytes[0]));
    pfind=(&findb[0]);
    casef=2;
    SearchAll();
    }

LOCAL_C VOID FindAgain(VOID)
    {
    LONG tot;

    if (fpos<0)
        {
        wInfoMsg("Nothing to find again");
        return;
        }
    tot=fpos+1;
    if (tot==last || DoSearch(&tot))
        wInfoMsg("No more found");
    }

LOCAL_C VOID ManageCommand(INT index)
    {
    switch (index)
        {
    case ITEMS_BEFORE_FILE:
        OpenFile();
        break;
    case ITEMS_BEFORE_FILE+1:
        WriteFile();
        break;
    case ITEMS_BEFORE_FIND:
        FindText();
        break;
    case ITEMS_BEFORE_FIND+1:
        FindBytes();
        break;
    case ITEMS_BEFORE_FIND+2:
        FindAgain();
        break;
    case ITEMS_BEFORE_SPECIAL:
        GotoLpos();
        break;
    case ITEMS_BEFORE_SPECIAL+1:
        p_exit(0);
        }
    }

LOCAL_C VOID MoveToTop(VOID)
    {
    lpos=0;
    DisplayPending=TRUE;
    }

LOCAL_C VOID CheckAndDisplay(VOID)
    {
    if (lpos<0)
        lpos=0;
    DisplayPending=TRUE;
    }

LOCAL_C VOID MoveToBottom(VOID)
    {
    lpos=last-64;
    CheckAndDisplay();
    }

LOCAL_C VOID MoveBy(INT delta)
    {
    lpos+=delta;
    if (lpos>last-64)
        lpos=last-64;
    CheckAndDisplay();
    }

LOCAL_C VOID EnsureDisplay(VOID)
    {
    if (DisplayPending)
        Display();
    }

LOCAL_C VOID TryExecuteCommand(INT keycode)
    {
    keycode=uLocateCommand(keycode);
    if (keycode>=0)
        ManageCommand(keycode);
    }

LOCAL_C VOID MainLoop(VOID)
    {
    INT ret;
    WMSG_KEY key;

    FOREVER
        {
        uGetKey(&key);
        if (key.keycode&W_EVENT_KEY)
            {
            if (key.keycode==CONS_EVENT_COMMAND)
                p_exit(0);
            }
        else if (key.keycode&W_SPECIAL_KEY)
            {
            EnsureDisplay();
            TryExecuteCommand(key.keycode&(~W_SPECIAL_KEY));
            }
        else
            {
            switch (key.keycode)
                {
            case W_KEY_MENU:
                if (!(key.modifiers&W_CTRL_MODIFIER))
                    {
                    EnsureDisplay();
                    ret=uPresentMenus();
                    if (ret>0)
                        TryExecuteCommand(ret);
                    }
                break;
            case W_KEY_LEFT:
                MoveBy(-1);
                break;
            case W_KEY_RIGHT:
                MoveBy(1);
                break;
            case W_KEY_UP:
                if (key.modifiers&W_CTRL_MODIFIER)
                    MoveToTop();
                else
                    MoveBy(-8);
                break;
            case W_KEY_DOWN:
                if (key.modifiers&W_CTRL_MODIFIER)
                    MoveToBottom();
                else
                    MoveBy(8);
                break;
            case W_KEY_HOME:
                MoveBy(-4);
                break;
            case W_KEY_END:
                MoveBy(4);
                break;
            case W_KEY_PAGE_UP:
                if (key.modifiers&W_CTRL_MODIFIER)
                    MoveToTop();
                else
                    MoveBy(-56);
                break;
            case W_KEY_PAGE_DOWN:
                if (key.modifiers&W_CTRL_MODIFIER)
                    MoveToBottom();
                else
                    MoveBy(56);
                break;
                }
            }
        if (DisplayPending && !uKeyPressOutstanding())
            Display();
        }
    }

LOCAL_C VOID CreateGC(VOID)
    {
    INT hgc;
    G_GC gc;

    gc.style=G_STY_MONO;
    hgc=gCreateGC(uFindMainWid(),G_GC_MASK_STYLE,&gc);
    if (hgc<0)
        p_exit(hgc);
    }

LOCAL_C VOID SpecificInit(VOID)
    {
    CreateGC();
    gBorder(W_BORD_CORNER_4);
    hCrackCommandLine();
    if (TryOpenFile(DatUsedPathNamePtr,&fcb))
        p_exit(0);
    ResetDisplay();
    }

GLDEF_C VOID main(VOID)
    {
    uCommonInit();
    SpecificInit();
    MainLoop();
    }
